test(ios-readplace): make the share extension logic testable#921
Closed
FagnerMartinsBrack wants to merge 2 commits into
Closed
test(ios-readplace): make the share extension logic testable#921FagnerMartinsBrack wants to merge 2 commits into
FagnerMartinsBrack wants to merge 2 commits into
Conversation
The rest of the repo enforces a 100% coverage gate, but the iOS suite ran with no coverage measurement or threshold at all — a structural blind spot. `make test` now runs with `-enableCodeCoverage` and fails if any source file drops below its recorded floor (`scripts/check-coverage.py`). The gate is a ratchet, not an all-at-once 100%: `scripts/coverage-baseline.json` excludes pure SwiftUI views and WebKit/OS-boundary glue (the Swift analog of the repo's whole-file OS-boundary exclusion) and records a per-file floor for every other file, seeded at today's coverage. A file with no floor must be 100%, so a new logic file cannot land untested. Later PRs raise the floors toward 100% and move ShareURLExtractor out of the exclusions once it is testable. The checker uses only the system python3 + xcrun, so the CI ios-tests job needs no extra toolchain setup.
`ShareViewController` and `ShareURLExtractor` lived only in the extension target, which no test target compiles, so their real logic — the multi-item payload scan, the URL-item type fallbacks, the PDF size ceiling, and the outcome→status mapping — had zero coverage and was structurally unreachable. - Move `ShareURLExtractor` into `Shared/` (it is Foundation-only) so it compiles into the app target `@testable import` can reach, and split `extract` into a context wrapper plus an item-list core a test can drive with fabricated `NSItemProvider`s instead of a live extension context. - Extract the outcome→(message, symbol, tone) decision out of `ShareViewController` into a pure `ShareStatusPresentation` in `Shared/`; the controller keeps only the tone→`UIColor` mapping at the UIKit boundary. - Cover both: `ShareURLExtractor` at 96% (URL / data / string / plain-text extraction, multi-item split, PDF provider + suggested-name fallback, the byte ceiling) and `ShareStatusPresentation` at 100% (every outcome, refusal join, error-vs-warning tone). - Coverage baseline: drop `ShareURLExtractor` from the exclusions and floor it at 96%; pin `ShareStatusPresentation` at 100%.
Base automatically changed from
ios/coverage-gate
to
ios/independent-contract-fixes
July 5, 2026 12:57
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
ShareViewControllerandShareURLExtractorwere compiled only into the extension target, which no test target sees. So their real branching logic — the multi-item payload scan, the URL-item type fallbacks (URL / Data / String / plain-text), the PDF size ceiling, and the outcome→status mapping — had zero coverage and was structurally untestable. A host delivering an unexpected payload shape would regress silently.What changed & the value
ShareURLExtractor→Shared/: it is Foundation-only, so moving it makes it compile into the app target that@testable import Readplacecan reach.extract(from:)is split into a thinNSExtensionContextwrapper plus an item-list core a test drives with fabricatedNSItemProviders — no live extension context (which no test can construct) required.ShareStatusPresentation: the seven-case outcome→(message, SF Symbol, tone) decision — including joining a refusal's messages and choosing error-vs-warning — moves out of the UIKit controller into a pure value inShared/.ShareViewControllerkeeps only the trivial tone→UIColormapping at the boundary.ShareURLExtractornow at 96% (URL/Data/String/plain-text extraction, multi-item split, PDF provider + suggested-name fallback, the byte ceiling),ShareStatusPresentationat 100% (every outcome, the refusal join, both tone branches).scripts/coverage-baseline.json):ShareURLExtractormoves out of the exclusions to a 96% floor;ShareStatusPresentationpinned at 100%. The gate now enforces 21 files (was 19), 9 excluded (was 10).Testing
Full suite green with the gate active locally (227 tests, +20;
iOS coverage gate passed: 21 files at or above floor, 9 excluded), plus the STAGING smoke pass.